using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace LogAnalyzer.Grabbers
{
    public class SocketGrabber : GrabberBase
    {
        private Socket _socketAccept = null;
        private Socket _socket = null;
        private Thread _listener = null;
        private Encoding _enc = null;
        private bool _exitListener = false;

        private Protocol Protocol
        {
            get
            {
                Protocol prot = Protocol.TCP;
                if (Config.ContainsKey("socketGrabber.protocol"))
                    try
                    {
                        prot = (Protocol)Enum.Parse(typeof(Protocol), Config["socketGrabber.protocol"]);
                    }
                    catch { }
                return prot;
            }
            set
            {
                if (Config.ContainsKey("socketGrabber.protocol"))
                    Config.Remove("socketGrabber.protocol");
                Config["socketGrabber.protocol"] = value.ToString();
            }
        }
        private IPAddress IPAddress
        {
            get
            {
                IPAddress ip = IPAddress.Parse("0.0.0.0");
                if (Config.ContainsKey("socketGrabber.ipAddress"))
                    ip = IPAddress.Parse(Config["socketGrabber.ipAddress"]);
                return ip;
            }
            set
            {
                if (Config.ContainsKey("socketGrabber.ipAddress"))
                    Config.Remove("socketGrabber.ipAddress");
                Config["socketGrabber.ipAddress"] = value.ToString();
            }
        }
        private int PortNumber
        {
            get
            {
                int port = 65000;
                if (Config.ContainsKey("socketGrabber.portNumber"))
                    int.TryParse(Config["socketGrabber.portNumber"], out port);
                return port;
            }
            set
            {
                if (Config.ContainsKey("socketGrabber.portNumber"))
                    Config.Remove("socketGrabber.portNumber");
                Config["socketGrabber.portNumber"] = value.ToString();
            }
        }
        private DataEncoding Encoding
        {
            get
            {
                DataEncoding enc = DataEncoding.UTF8;
                if (Config.ContainsKey("socketGrabber.encoding"))
                    try
                    {
                        enc = (DataEncoding)Enum.Parse(typeof(DataEncoding), Config["socketGrabber.encoding"]);
                    }
                    catch { }
                return enc;
            }
            set
            {
                if (Config.ContainsKey("socketGrabber.encoding"))
                    Config.Remove("socketGrabber.encoding");
                Config["socketGrabber.encoding"] = value.ToString();
            }
        }




        public override string TextForViewer
        {
            get
            {
                string prot = "";
                switch (Protocol)
                {
                    case Protocol.TCP: prot = "TCP"; break;
                    case Protocol.UDP: prot = "UDP"; break;
                }
                return string.Format("{0} {1}:{2}",
                    prot,
                    IPAddress,
                    PortNumber
                    );
            }
        }

        public override DialogResult ShowConfigDialog()
        {
            using (Dialogs.SocketGrabberCfg cfg = new LogAnalyzer.Grabbers.Dialogs.SocketGrabberCfg())
            {
                cfg.Protocol = this.Protocol;
                cfg.IPAddress = this.IPAddress;
                cfg.PortNumber = this.PortNumber;
                cfg.DataEncoding = this.Encoding;

                DialogResult dlgRes = cfg.ShowDialog();

                if (dlgRes == DialogResult.OK)
                {
                    this.Protocol = cfg.Protocol;
                    this.IPAddress = cfg.IPAddress;
                    this.PortNumber = cfg.PortNumber;
                    this.Encoding = cfg.DataEncoding;
                }

                return dlgRes;
            }
        }

        protected override void StartGrab(bool isRestart)
        {
            switch (this.Encoding)
            {
                case DataEncoding.ASCII:
                    _enc = System.Text.Encoding.ASCII;
                    break;
                case DataEncoding.BigEndianUnicode:
                    _enc = System.Text.Encoding.BigEndianUnicode;
                    break;
                case DataEncoding.Unicode:
                    _enc = System.Text.Encoding.Unicode;
                    break;
                case DataEncoding.UTF32:
                    _enc = System.Text.Encoding.UTF32;
                    break;
                case DataEncoding.UTF7:
                    _enc = System.Text.Encoding.UTF7;
                    break;
                case DataEncoding.UTF8:
                    _enc = System.Text.Encoding.UTF8;
                    break;
                default:
                    break;
            }

            _exitListener = false;
            _listener = new Thread(Listener);
            _listener.Start();
        }

        public override void StopGrab()
        {
            _exitListener = true;

            if (_socketAccept != null) _socketAccept.Close();
            if (_socket != null) _socket.Close();
            if (_listener != null) _listener.Join();
            _listener = null;
            _socket = null;
        }

        public override void Dispose()
        {
            StopGrab();
        }


        private void Listener()
        {
            switch (this.Protocol)
            {
                case Protocol.TCP:
                    _socketAccept = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    _socketAccept.Bind(new IPEndPoint(this.IPAddress, this.PortNumber));
                    _socketAccept.Listen(1);
                    IAsyncResult tag = _socketAccept.BeginAccept(null, null);
                    tag.AsyncWaitHandle.WaitOne();
                    if (_exitListener) return;
                    _socket = _socketAccept.EndAccept(tag);
                    _socketAccept.Close();
                    _socketAccept = null;
                    break;


                case Protocol.UDP:
                    _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Udp);
                    _socket.Bind(new IPEndPoint(this.IPAddress, this.PortNumber));
                    break;
            }


            while (!_exitListener)
            {
                byte[] buffer = new byte[10240];
                IAsyncResult tag = _socket.BeginReceive(buffer, 0, 10240, SocketFlags.None, null, null);
                tag.AsyncWaitHandle.WaitOne();

                if (_exitListener)
                    break;

                int receivedBytes = 0;
                if (_socket != null)
                    receivedBytes = _socket.EndReceive(tag);

                if (receivedBytes > 0)
                {
                    HandleNewString(_enc.GetString(buffer, 0, receivedBytes));
                }
                else
                {
                    // remote part has closed connection
                    StartGrab();
                    break;
                }
            }
        }

    }


    public enum Protocol
    { 
        TCP,
        UDP,
    }

}
